﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Happy.Query.Filters.Internal
{
    internal class ValidFilterBuilder : FilterVisitor
    {
        private readonly IDictionary<string, Parameter> _parameters;
        private readonly IDictionary<string, object> _arguments;
        private readonly Stack<StackItem> _stack = new Stack<StackItem>();
        private IFilter _resultFilter;

        public ValidFilterBuilder(IDictionary<string, Parameter> parameters, IDictionary<string, object> arguments)
        {
            _parameters = parameters;
            _arguments = arguments;
        }

        public IFilter GetFilter()
        {
            return _resultFilter;
        }

        public override void Visit(FilterGroup group)
        {
            _stack.Push(new StackItem());

            base.Visit(group);

            var stackItem = _stack.Pop();
            if (stackItem.NewGroupFilters.Count > 0)
            {
                var newConnector = this.GetNewConnector(group.Connector, this.IsFirstFilter());
                var newFilterGroup = new FilterGroup(newConnector, stackItem.NewGroupFilters.ToArray());

                if (_stack.Count >= 1)
                {
                    _stack.Peek().NewGroupFilters.Add(newFilterGroup);
                }
                else
                {
                    _resultFilter = newFilterGroup;
                }
            }
        }

        public override void Visit(Filter filter)
        {
            foreach (var operand in filter.Operands)
            {
                if (operand.OperandType != OperandType.Parameter)
                {
                    continue;
                }

                var parameterName = operand.Value.ToString();
                if (!_parameters.ContainsKey(parameterName))
                {
                    throw new InvalidDataException(string.Format(Resource.Messages.Error_ParameterUnDefined, parameterName));
                }

                if (!_arguments.ContainsKey(parameterName))
                {
                    if (_parameters[parameterName].Required)
                    {
                        throw new InvalidDataException(string.Format(Resource.Messages.Error_ParameterIsRequired, parameterName));
                    }

                    return;
                }
            }

            var newConnector = this.GetNewConnector(filter.Connector, this.IsFirstFilter());
            var newFilter = new Filter(newConnector, filter.Property, filter.Operator, filter.Operands);

            if (_stack.Count >= 1)
            {
                _stack.Peek().NewGroupFilters.Add(newFilter);
            }
            else
            {
                _resultFilter = newFilter;
            }
        }

        private Boolean IsFirstFilter()
        {
            return _stack.Count == 0 || _stack.Peek().NewGroupFilters.Count == 0;
        }

        private Connector GetNewConnector(Connector oldConnector, Boolean isFirstFilter)
        {
            if (!isFirstFilter)
            {
                return oldConnector;
            }

            switch (oldConnector)
            {
                case Connector.Empty:
                case Connector.And:
                case Connector.Or:
                    return Connector.Empty;
                case Connector.Not:
                case Connector.AndNot:
                case Connector.OrNot:
                    return Connector.Not;
            }

            return oldConnector;
        }

        private class StackItem
        {
            public StackItem()
            {
                this.NewGroupFilters = new List<IFilter>();
            }

            public List<IFilter> NewGroupFilters { get; private set; }
        }
    }
}
